
/**
\file
Defines the rainbow::draw_command class.
\author Sean Tromans
*/

#include <sun_pre_compiled.hpp>
#include <rainbow/draw_command/draw_command.hpp>

#if !defined(SUN_DISABLE_LUA)
#include <sun/lua/lua.hpp>
#endif

namespace rainbow
{

	/**
	Default Constructor.
	@param device The graphics device this draw_command is intended for.
	\author Sean Tromans
	*/
	draw_command::draw_command( graphics_device & device )
		:		device(&device)

			,	blend_handle(1)
			,	culling_handle(1)
			,	depth_handle(1)
			,	stencil_handle(1)
	{
	}

	/**
	Determines whether the draw_command is valid.
	\author Sean Tromans
	*/
	bool draw_command::is_valid( ) const
	{
		return device != nullptr;
	}

	/**
	Exposes the draw_command to lua.
	@param L The Lua State being initialised.
	\author Sean Tromans.
	*/
	void draw_command::lua_interface( lua_State * L )
	{
#if !defined(SUN_DISABLE_LUA)
		luabind::module( L, "rainbow" )
		[
			luabind::class_< draw_command >( "draw_command" )
				.def( luabind::constructor<graphics_device &>( ) )
				.def_readonly(	"device", &draw_command::device )

				.def_readonly(	"blend_handle",		&draw_command::blend_handle )
				.def_readonly(	"culling_handle",	&draw_command::culling_handle )
				.def_readonly(	"depth_handle",		&draw_command::depth_handle )
				.def_readonly(	"stencil_handle",	&draw_command::stencil_handle )

				.scope
				[
					luabind::def( "default_ordering",			draw_command::default_ordering ),
					luabind::def( "order_without_transparency",	draw_command::order_without_transparency )
				]
		];
#else
		NMN_UNUSED( L );
#endif
	}

	/**
	The default ordering of draw_commands.
	Orders opaque objects ahead of transparent objects.
	If opaque sorts by material then from front to back.
	If transparent sorts from back to front, then by material.
	@param a The first draw_command to compare.
	@param b The first draw_command to compare.
	@returns true if a compares less than b. False otherwise.
	\author Sean Tromans
	*/
	bool draw_command::default_ordering( draw_command const & a, draw_command const & b )
	{
		if ( a.device != b.device )
		{
			throw different_device_targets_exception( );
		}

		if ( !a.is_valid( ) || !b.is_valid( ) )
		{
			throw invalid_draw_command_exception( );
		}

		blend_state const & blend_a = a.device->get_blend_state( a.blend_handle );
		blend_state const & blend_b = b.device->get_blend_state( b.blend_handle );

		if ( blend_a.enabled == blend_b.enabled )
		{
			if ( !blend_a.enabled )
			{
				return order_without_transparency( a, b );
			}
			else
			{
				//	SORT BACK TO FRONT
				return false;
			}
		}
		else if ( blend_a.enabled && !blend_b.enabled )
		{
			return false;
		}
		else // if ( blend_b.enabled && !blend_a.enabled )
		{
			return true;
		}
	}

	/**
	Similar to the default ordering mechanism, but ignored the opacity setting of the command.
	First sorts by material and then sorts from front-to-back.
	@param a The first draw_command to compare.
	@param b The first draw_command to compare.
	@returns true if a compares less than b. False otherwise.
	\author Sean Tromans
	*/
	bool draw_command::order_without_transparency( draw_command const & a, draw_command const & b )
	{
		if ( a.device != b.device )
		{
			throw different_device_targets_exception( );
		}

		if ( !a.is_valid( ) || !b.is_valid( ) )
		{
			throw invalid_draw_command_exception( );
		}

		if ( a.blend_handle != b.blend_handle )
		{
			return a.blend_handle < b.blend_handle;
		}
		else if ( a.stencil_handle != b.stencil_handle )
		{
			return a.stencil_handle < b.stencil_handle;
		}
		else if ( a.depth_handle != b.depth_handle )
		{
			return a.depth_handle < b.depth_handle;
		}
		else if ( a.culling_handle != b.culling_handle )
		{
			return a.culling_handle < b.culling_handle;
		}
		else
		{
			return false;
		}
	}

};
